package com.cdn.allmobilestore.ulti;

import java.io.File;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Timer;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.CopyOnWriteArraySet;

import android.app.AlertDialog;
import android.app.DownloadManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.pm.IPackageInstallObserver;
import android.content.pm.PackageManager;
import android.database.ContentObserver;
import android.database.Cursor;
import android.net.Uri;
import android.os.Build;
import android.os.Environment;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Looper;
import android.os.RemoteException;
import android.text.TextUtils;
import android.util.SparseArray;
import android.view.WindowManager;
import android.widget.Toast;

import com.cdn.allmobilestore.model.AppInfo;
 
public class DownloadInstallManager {
	public final int INSTALL_REPLACE_EXISTING = 2;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} on success.
     * @hide
     */
    public static final int INSTALL_SUCCEEDED = 1;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package is
     * already installed.
     * @hide
     */
    public static final int INSTALL_FAILED_ALREADY_EXISTS = -1;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package archive
     * file is invalid.
     * @hide
     */
    public static final int INSTALL_FAILED_INVALID_APK = -2;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the URI passed in
     * is invalid.
     * @hide
     */
    public static final int INSTALL_FAILED_INVALID_URI = -3;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if the package manager
     * service found that the device didn't have enough storage space to install the app.
     * @hide
     */
    public static final int INSTALL_FAILED_INSUFFICIENT_STORAGE = -4;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if a
     * package is already installed with the same name.
     * @hide
     */
    public static final int INSTALL_FAILED_DUPLICATE_PACKAGE = -5;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the requested shared user does not exist.
     * @hide
     */
    public static final int INSTALL_FAILED_NO_SHARED_USER = -6;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * a previously installed package of the same name has a different signature
     * than the new package (and the old package's data was not removed).
     * @hide
     */
    public static final int INSTALL_FAILED_UPDATE_INCOMPATIBLE = -7;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package is requested a shared user which is already installed on the
     * device and does not have matching signature.
     * @hide
     */
    public static final int INSTALL_FAILED_SHARED_USER_INCOMPATIBLE = -8;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package uses a shared library that is not available.
     * @hide
     */
    public static final int INSTALL_FAILED_MISSING_SHARED_LIBRARY = -9;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package uses a shared library that is not available.
     * @hide
     */
    public static final int INSTALL_FAILED_REPLACE_COULDNT_DELETE = -10;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package failed while optimizing and validating its dex files,
     * either because there was not enough storage or the validation failed.
     * @hide
     */
    public static final int INSTALL_FAILED_DEXOPT = -11;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package failed because the current SDK version is older than
     * that required by the package.
     * @hide
     */
    public static final int INSTALL_FAILED_OLDER_SDK = -12;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package failed because it contains a content provider with the
     * same authority as a provider already installed in the system.
     * @hide
     */
    public static final int INSTALL_FAILED_CONFLICTING_PROVIDER = -13;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package failed because the current SDK version is newer than
     * that required by the package.
     * @hide
     */
    public static final int INSTALL_FAILED_NEWER_SDK = -14;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package failed because it has specified that it is a test-only
     * package and the caller has not supplied the {@link #INSTALL_ALLOW_TEST}
     * flag.
     * @hide
     */
    public static final int INSTALL_FAILED_TEST_ONLY = -15;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the package being installed contains native code, but none that is
     * compatible with the the device's CPU_ABI.
     * @hide
     */
    public static final int INSTALL_FAILED_CPU_ABI_INCOMPATIBLE = -16;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package uses a feature that is not available.
     * @hide
     */
    public static final int INSTALL_FAILED_MISSING_FEATURE = -17;

    // ------ Errors related to sdcard
    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * a secure container mount point couldn't be accessed on external media.
     * @hide
     */
    public static final int INSTALL_FAILED_CONTAINER_ERROR = -18;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package couldn't be installed in the specified install
     * location.
     * @hide
     */
    public static final int INSTALL_FAILED_INVALID_INSTALL_LOCATION = -19;

    /**
     * Installation return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)} if
     * the new package couldn't be installed in the specified install
     * location because the media is not available.
     * @hide
     */
    public static final int INSTALL_FAILED_MEDIA_UNAVAILABLE = -20;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser was given a path that is not a file, or does not end with the expected
     * '.apk' extension.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_NOT_APK = -100;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser was unable to retrieve the AndroidManifest.xml file.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_BAD_MANIFEST = -101;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser encountered an unexpected exception.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_UNEXPECTED_EXCEPTION = -102;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser did not find any certificates in the .apk.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_NO_CERTIFICATES = -103;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser found inconsistent certificates on the files in the .apk.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_INCONSISTENT_CERTIFICATES = -104;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser encountered a CertificateEncodingException in one of the
     * files in the .apk.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_CERTIFICATE_ENCODING = -105;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser encountered a bad or missing package name in the manifest.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_BAD_PACKAGE_NAME = -106;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser encountered a bad shared user id name in the manifest.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_BAD_SHARED_USER_ID = -107;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser encountered some structural problem in the manifest.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_MANIFEST_MALFORMED = -108;

    /**
     * Installation parse return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the parser did not find any actionable tags (instrumentation or application)
     * in the manifest.
     * @hide
     */
    public static final int INSTALL_PARSE_FAILED_MANIFEST_EMPTY = -109;

    /**
     * Installation failed return code: this is passed to the {@link IPackageInstallObserver} by
     * {@link #installPackage(android.net.Uri, IPackageInstallObserver, int)}
     * if the system failed to install the package because of system issues.
     * @hide
     */
    public static final int INSTALL_FAILED_INTERNAL_ERROR = -110;

	private static DownloadInstallManager sDownloadInstallManager;
	public Context mContext;
    private HandlerThread mDownloadTaskManageThread;
    private HandlerThread mInstallThread;
    private final File mCacheDir;
	private ArrayList<String> mDownloadInstallApps;
	public final ConcurrentHashMap<String, AppInfo> mInstallManuallyMap;
	public DownloadManager mDownloadManager;
	private InstallHandler mInstallHandler;
	private final DownloadInstallMonitor mDownloadInstallMonitor;
	public ConcurrentHashMap<Long, String> mAppDownloadMap;
	private ConcurrentHashMap<String, Progress> mCurrProgressMap;
	private ConcurrentHashMap<String, CopyOnWriteArraySet<ProgressListener>> mProgressListeners;
	private HandlerThread mProgressThread;
	private ProgressHandler mProgressHandler;
	private DownloadTaskManageHandler mDownloadTaskManageHandler;
	private HashSet<TaskListener> mTaskListeners;
	private LocalAppManager mLocalAppManager = LocalAppManager.getManager();
	Method method;
	private OnInstalledPackaged onInstalledPackaged;
	PackageManager pm;
	private com.cdn.allmobilestore.ulti.DownloadInstallManager.InstallHandler.PackageInstallObserver observer;
	

	
	public DownloadInstallManager(Context mContext) throws SecurityException, NoSuchMethodException {
		this.mContext = mContext;
		this.mDownloadManager 		= (DownloadManager) mContext.getSystemService(Context.DOWNLOAD_SERVICE);
		this.mAppDownloadMap 		= new ConcurrentHashMap<Long, String>();
		this.mDownloadInstallApps 	= new ArrayList<String>();
		this.mInstallManuallyMap = new ConcurrentHashMap<String, AppInfo>();
		this.mDownloadInstallMonitor = new DownloadInstallMonitor();
		this.mProgressListeners 	= new ConcurrentHashMap<String, CopyOnWriteArraySet<ProgressListener>>();
		this.mCurrProgressMap 		= new ConcurrentHashMap<String, Progress>();
		this.mTaskListeners 		= new HashSet<TaskListener>();
		this.mCacheDir = mContext.getDir("apks", Context.MODE_WORLD_READABLE);
	    if (!this.mCacheDir.exists());
	    try
	    {
	      this.mCacheDir.mkdirs();	      
	    }
	    catch (SecurityException localSecurityException)
	    {
	    	Log.v("cannot create apks dir");
	    }
		Class<?>[] types = new Class[] {Uri.class, IPackageInstallObserver.class, int.class, String.class};
		this.pm	=  this.mContext.getPackageManager();
		
		method 	= pm.getClass().getMethod("installPackage", types);
		
	
	}
	
	
	
	public static void init(Context context) throws SecurityException, NoSuchMethodException
	{
		if(sDownloadInstallManager == null)
			sDownloadInstallManager = new DownloadInstallManager(context);
	}
	
	public static DownloadInstallManager getManager()
	{
		return sDownloadInstallManager;
	}
	
	
	public void setOnInstalledPackaged(OnInstalledPackaged onInstalledPackaged) {
		this.onInstalledPackaged = onInstalledPackaged;
	}

	public void installPackage(String apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		installPackage(new File(apkFile));
	}

	public void installPackage(File apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		if (!apkFile.exists()) throw new IllegalArgumentException();
		Uri packageURI = Uri.fromFile(apkFile);
		installPackage(packageURI);
	}

	public void installPackage(Uri apkFile) throws IllegalArgumentException, IllegalAccessException, InvocationTargetException {
		method.invoke(pm, new Object[] {apkFile, observer, INSTALL_REPLACE_EXISTING, null});
	}
	
	public void handleManualPackageInstall(String packagename)
	  {
	   if (this.mInstallManuallyMap.containsKey(packagename))
	   {
		   AppInfo appInfo = AppInfo.getAppByPackage(packagename);
		  
		   if(Build.VERSION.SDK_INT < 11)
		   {
			   File f = new File(Environment.DIRECTORY_DOWNLOADS + appInfo.getPackageName() + ".apk") ;
			   f.delete();
		   }
		   GlobalApplication.createShortcutForPackage(packagename);
      	   DownloadInstallManager.this.mInstallHandler.installComplete(appInfo.getId(), appInfo.getDownloadId(), 17);
      	   
      	 /*
		   Iterator<Long> dlid = DownloadInstallManager.this.mAppDownloadMap.keySet().iterator();
	       while(dlid.hasNext()){
	            Long key = dlid.next();
	            String appid = DownloadInstallManager.this.mAppDownloadMap.get(key);
	            if(appid.equals(appInfo.getId())){
	            	 
	            }
	        }
		  */
   
	   }

	  }
	
	public void addProgressListener(String appid, ProgressListener listener)
	  {
	    if ((listener == null) || (TextUtils.isEmpty(appid)))
	    	return;
	    else
	    {	     
	      synchronized (this.mProgressListeners)
	      {
	        CopyOnWriteArraySet<ProgressListener> localCopyOnWriteArraySet = (CopyOnWriteArraySet<ProgressListener>)this.mProgressListeners.get(appid);
	        if (localCopyOnWriteArraySet == null)
	        {
	          localCopyOnWriteArraySet = new CopyOnWriteArraySet<ProgressListener>();
	          this.mProgressListeners.put(appid, localCopyOnWriteArraySet);
	        }
	        localCopyOnWriteArraySet.add(listener);
	        listener.onProgressUpdate(appid, (Progress)this.mCurrProgressMap.get(appid));
	      }
	    }
	  }
		
	public void removeProgressListener(String appId, ProgressListener listener)
	  {
	    if ((listener == null) || (TextUtils.isEmpty(appId)))	    
	    	return;
	    else
	      synchronized (this.mProgressListeners)
	      {
	        CopyOnWriteArraySet<ProgressListener> localCopyOnWriteArraySet = this.mProgressListeners.get(appId);
	        if (localCopyOnWriteArraySet != null)
	          localCopyOnWriteArraySet.remove(listener);
	      }
	    
	  }
	
	public void handleDownloadComplete(long downloadid)
	  {
	    this.mDownloadTaskManageHandler.handleDownloadComplete(downloadid);
	  }
	
	
	private class DownloadTaskManageHandler extends Handler
	  {
	    private volatile int mDownloadingCount = 0;
	    private ConcurrentLinkedQueue<AppInfo> mWaitingQueue = new ConcurrentLinkedQueue<AppInfo>();

	    public DownloadTaskManageHandler(Looper arg2)
	    {
	      super();
	      initialize();
	    }
	    
	    private void initialize()
	    {
	      post(new Runnable()
	      {
	        public void run()
	        {
	          //DownloadInstallManager.DownloadTaskManageHandler.this.reloadDownloadingTasks();
	        }
	      });
	    }
	    
	
		public void handleDownloadComplete(final long id) {
			
			post(new Runnable()
		      {
		        public void run()
		        {
		        	Log.v("mAppDownloadMap size:" + DownloadInstallManager.this.mAppDownloadMap.size());
		        	
		        	String appid = (String)DownloadInstallManager.this.mAppDownloadMap.get(Long.valueOf(id));
		        	Log.v("handleDownload Complete: appid" + appid + " id:" + id);
		        	if (TextUtils.isEmpty(appid))
		        	{
		        		Log.v("return1:" + appid);
		        		return;
		        	}
		        	else
		        	{
		        		Log.v("handleDownloadComplete1:" + appid);
		        		
		                DownloadManager.Query query = new DownloadManager.Query();
		                query.setFilterById(id);
		                Cursor cursor =  DownloadInstallManager.this.mDownloadManager.query(query);
		                if (cursor != null)
		                try
		                {
		                	Log.v("handleDownloadComplete2:" + appid);
		                	if (cursor.moveToFirst())
		                	{
		                		Log.v("handleDownloadComplete3:" + appid);
		                		String uri = ""; 
		                		
		                		if(Build.VERSION.SDK_INT >= 11)
		                			uri = cursor.getString(cursor.getColumnIndexOrThrow("local_filename"));
		                				                		
		                	//	File mFile = new File(Uri.parse(uri).getPath());
		                		int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
		                		
		                		Log.v("download status: id:" + id + " status:" + status + " uri:" + uri);		                		
		                		if (status != -1)
		                		{
		                			if (status == DownloadManager.STATUS_FAILED)
		                            {
		                              DownloadInstallManager.DownloadTaskManageHandler.this.downloadFail(id, DownloadManager.PAUSED_UNKNOWN);
		                            }
		                            else if (TextUtils.isEmpty(uri))
		                            {
		                              DownloadInstallManager.DownloadTaskManageHandler.this.downloadFail(id,  DownloadManager.PAUSED_UNKNOWN);
		                            }
		                           
		                            else
		                            {
		                            	Log.v("download complete: prepare to install");
		                              DownloadInstallManager.DownloadTaskManageHandler.this.downloadSuccess(id);
		                              DownloadInstallManager.this.mInstallHandler.install(appid, id, uri);
		                            }
		                		}
		                		else
		                		{
		                			DownloadInstallManager.DownloadTaskManageHandler.this.downloadFail(id,status);
		                		}
		                		if (cursor != null)
			                		cursor.close();
		                	}
		                	else
		                	{
		                		DownloadInstallManager.DownloadTaskManageHandler.this.downloadFail(id,DownloadManager.STATUS_FAILED);
		                	}
		                }catch(Exception e){
		                	Log.v("handleDownloadComplete exception:" +  e.getMessage());
		                }
		                finally{
		                	if (cursor != null)
		                		cursor.close();
		                }
		        	}
		        }
		      });
			
		}
		
		
		
		protected void downloadSuccess(long downloadId) {
			Log.v("downloadSuccess");
			 DownloadInstallManager.this.mAppDownloadMap.remove(Long.valueOf(downloadId));
		}

		protected void downloadFail(long downloadId, final int error) {
			Log.v("download fail status:" + error);
		
			final String appid = DownloadInstallManager.this.mAppDownloadMap.remove(Long.valueOf(downloadId));
			DownloadInstallManager.this.mInstallHandler.post(new Runnable()
	        {
	          public void run()
	          {
	            DownloadInstallManager.this.mDownloadInstallMonitor.onFail(appid, error);
	          }
	        });
			
			
			
			synchronized (DownloadInstallManager.this.mInstallManuallyMap)
			{
				DownloadInstallManager.this.mInstallManuallyMap.remove(AppInfo.get(appid).getPackageName());
			}
			synchronized (DownloadInstallManager.this.mDownloadInstallApps)
	        {
				DownloadInstallManager.this.mDownloadInstallApps.remove(appid);
				DownloadInstallManager.this.mCurrProgressMap.remove(appid);				
	        }
			synchronized (DownloadInstallManager.this.mProgressListeners)
	        {
				DownloadInstallManager.this.mProgressListeners.remove(appid);
				Iterator<TaskListener> iterator = DownloadInstallManager.this.mTaskListeners.iterator();
		          while (iterator.hasNext())
		          {
		            ((DownloadInstallManager.TaskListener)iterator.next()).onTaskFail(appid,error);		          
		          }
	        }
			checkAndArrangeNext();
		}

		private long downloadInternal(String uri, AppInfo appInfo)throws IllegalArgumentException
	    {
			Log.v("download Internal:" + uri);			
	      DownloadManager.Request request = new DownloadManager.Request(Uri.parse(uri));
	      request.setTitle(appInfo.getName());	     
	      request.setMimeType("application/vnd.android.package-archive");
	      //Restrict the types of networks over which this download may proceed.
	      request.setShowRunningNotification(true);  
	      
	      request.setAllowedNetworkTypes(DownloadManager.Request.NETWORK_WIFI | DownloadManager.Request.NETWORK_MOBILE);
	      if(Build.VERSION.SDK_INT < 11)
	      {
	    	  File f = new File(Environment.DIRECTORY_DOWNLOADS + appInfo.getPackageName() + ".apk") ;  
	    	  Uri dst_uri = Uri.fromFile(f);
		      request.setDestinationUri(dst_uri);
	    	  
	      }
	
	      //Set whether this download may proceed over a roaming connection.
	      request.setAllowedOverRoaming(false);
	      return DownloadInstallManager.this.mDownloadManager.enqueue(request);
	    }

		private void startConnectAndDownload(final AppInfo appInfo, final boolean isReloaded)
	    {
	      new Thread(new Runnable()
	      {
	        public void run()
	        {	AppInfo localappInfo = appInfo;	        	
	        	DownloadInstallManager.this.mProgressHandler.updateProgress(localappInfo.getId(), 1);
	        	DownloadInstallManager.DownloadTaskManageHandler downloadTaskManager = DownloadInstallManager.DownloadTaskManageHandler.this;
	        	long downloadId = downloadTaskManager.downloadInternal(appInfo.getLinkApk(), localappInfo);
	        	DownloadInstallManager.this.mAppDownloadMap.put(Long.valueOf(downloadId), localappInfo.getId());
	        	Log.v("startConnectAndDownload appid:" + appInfo.getId() + " id:" + downloadId);
	        	AppInfo.get(localappInfo.getId()).setDownloadId(downloadId);	        
	        }
	      }).start();
	    }
		
		public void arrange(final AppInfo appInfo) {
			post(new Runnable()
		      {
		        public void run()
		        {
		        	try{
		        		if(appInfo != null)
		        		{
	        			  synchronized (DownloadInstallManager.this.mDownloadInstallApps)
		  		          {
		  		        	  Log.v("add pakage:" + appInfo.getPackageName());
		  		            if (!DownloadInstallManager.this.mDownloadInstallApps.contains(appInfo.getId()))
		  		              DownloadInstallManager.this.mDownloadInstallApps.add(appInfo.getId());
		  		  		            
		  		            DownloadInstallManager.this.mInstallHandler.post(new Runnable()
		  		            {
		  		              public void run()
		  		              {
		  		                DownloadInstallManager.this.mDownloadInstallMonitor.onStart(appInfo.getId());
		  		              }
		  		            });
		  		            
		  		            Iterator<TaskListener> iterator = DownloadInstallManager.this.mTaskListeners.iterator();
		  		            if (iterator.hasNext())
		  		              ((DownloadInstallManager.TaskListener)iterator.next()).onTaskStart(appInfo.getId());
		  	
		  		          } 
		  		          synchronized (DownloadInstallManager.DownloadTaskManageHandler.this.mWaitingQueue)
		  		          {
		  					Log.v("DownloadTaskManageHandler.this.mWaitingQueue");
		  					Log.v("DownloadTaskManageHandler.this.mWaitingQueue:" + DownloadInstallManager.DownloadTaskManageHandler.this.mDownloadingCount);
		  					if ((DownloadInstallManager.DownloadTaskManageHandler.this.mWaitingQueue.isEmpty()) && (DownloadInstallManager.DownloadTaskManageHandler.this.mDownloadingCount < 2))
		  					{
		  					  Log.v("start startConnectAndDownload");		            
		  					  DownloadInstallManager.DownloadTaskManageHandler.this.startConnectAndDownload(appInfo,false);		
		  					  return;
		  					}
		  					DownloadInstallManager.DownloadTaskManageHandler.this.mWaitingQueue.add(appInfo);
		  					DownloadInstallManager.this.mProgressHandler.updateProgress(appInfo.getId(), 0);
		  		          }
		        		}
		        	}catch(Exception e)
		        	{
		        		Log.v("DownloadInstall.arrange Exception:"+ e.getMessage());
		        	}
		          
		        }
		      });
			
		}

		public void checkAndArrangeNext() {
			Log.v("checkAndArrangeNext");
			post(new Runnable()
		      {
		        public void run()
		        {
		          synchronized (DownloadInstallManager.DownloadTaskManageHandler.this.mWaitingQueue)
		          {		            
		            AppInfo appinfo = (AppInfo)DownloadInstallManager.DownloadTaskManageHandler.this.mWaitingQueue.poll();
		            if (appinfo != null)
		            {		             
		              DownloadInstallManager.DownloadTaskManageHandler.this.startConnectAndDownload(appinfo, false);
		            }
		            return;
		          }
		        }
		      });
			
		}
	 }
	
	
	 public class InstallHandler extends Handler
	 {
	    public InstallHandler(Looper arg2)
	    {
	      super();
	    }
	    
	    private String getPatchedApkPath(String appName)
	    {
	      return DownloadInstallManager.this.mCacheDir.getAbsolutePath() + "/" + appName + ".apk";
	    }
	    
		public void install(String appid, long id, String uri) throws IOException { 
			Log.v("install:" + Uri.parse(uri).getPath());
			DownloadInstallManager.this.mProgressHandler.updateProgress(appid, 5);
			Log.v("install 1");
			DownloadInstallManager.this.mProgressHandler.updateProgress(appid, 6);
			Log.v("install 2");
			//DownloadInstallManager.this.mContext.enforceCallingOrSelfPermission("android.permission.INSTALL_PACKAGES", null);
	       // DownloadInstallManager.this.mContext.getPackageManager().installPackage(Uri.parse(uri), new DownloadInstallManager.InstallHandler.PackageInstallObserver(DownloadInstallManager.InstallHandler.this,  id, uri), 2, DownloadInstallManager.this.mContext.getPackageName());

			Log.v("install 3");
			AppInfo appinfo = AppInfo.get(appid);
			Log.v("install appinfo:" + appinfo.getName());
			Log.v("install 4");
			GlobalApplication.showInstallingNotification(appinfo);
			File file = null;
			if(Build.VERSION.SDK_INT >= 11)
			{
				file = new File(uri);
			}
			else
			{
				file = new File(Environment.DIRECTORY_DOWNLOADS + appinfo.getPackageName() + ".apk"); 
			}
				
			Uri downloadFile = Uri.fromFile(file);
			
			try{				
			observer = new DownloadInstallManager.InstallHandler.PackageInstallObserver(appinfo,  id, uri);
			method.invoke(pm, new Object[] {downloadFile, observer, INSTALL_REPLACE_EXISTING, null});
	
			}catch(Exception e)
			{
				
				e.printStackTrace();
				//File mFile = new File(Uri.parse(uri).getPath());
				Log.v("install app:" + uri);	
				DownloadInstallManager.this.mInstallManuallyMap.put(appinfo.getPackageName(), appinfo);
	            Intent localIntent = new Intent(Intent.ACTION_VIEW);	            
	            localIntent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);  
	            localIntent.setDataAndType(downloadFile, "application/vnd.android.package-archive");
	            DownloadInstallManager.this.mContext.startActivity(localIntent);
	           
	            //
			}	
				
		}
		
		
		 private void installComplete(String appId, long downloadId, int error) {
			 Log.v("installComplete called:" + appId);
			 Iterator<TaskListener> iterator = DownloadInstallManager.this.mTaskListeners.iterator();
			 DownloadInstallManager.this.mDownloadInstallApps.remove(appId);
			 if (!iterator.hasNext())
			 {
				 Log.v("iterator not has next");
				 DownloadInstallManager.this.mDownloadTaskManageHandler.checkAndArrangeNext(); 
			 }				 			
			 else
			 {
				  Log.v("onTaskSuccess called");
				 ((DownloadInstallManager.TaskListener)iterator.next()).onTaskSuccess(appId);
			 }
	           	            		
			 synchronized (DownloadInstallManager.this.mProgressListeners)
	          {
				 DownloadInstallManager.this.mProgressListeners.remove(appId);
				 DownloadInstallManager.this.mDownloadInstallMonitor.onSuccess(appId);		           
	          }
			 
			 DownloadInstallManager.this.mAppDownloadMap.remove(downloadId);
		}
		
		 
		 public class PackageDeleteObserver implements LocalAppManager.LocalAppInstallRemoveListener{
	      private String mApkPath;
	      private String mAppId;
	      private long mDownloadId;
	      private Timer mTimer;
	      
	      public PackageDeleteObserver(String appid, long dlid)
	      {
	        this.mDownloadId = dlid;
	        this.mAppId = appid;
	        this.mTimer = new Timer();
	        this.mTimer.schedule(new TimerTask()
	        {
	          public void run()
	          {
	            //DownloadInstallManager.this.mInstallHandler.(DownloadInstallManager.InstallHandler.PackageDeleteObserver.this.mAppId, DownloadInstallManager.InstallHandler.PackageDeleteObserver.this.mDownloadId, 15);
	            DownloadInstallManager.this.mLocalAppManager.removeLocalAppListener(DownloadInstallManager.InstallHandler.PackageDeleteObserver.this);
	          }
	        }
	        , 10000L);
	      }

	      public void onAppInstalled(String appid, int dlid)
	      {
	      }

	      public void onAppRemoved(String appid, int downloadid)
	      {
	        this.mTimer.cancel();
	        DownloadInstallManager.InstallHandler.this.post(new Runnable()
	        {
	          public void run()
	          {	     
	              //DownloadInstallManager.this.mInstallHandler.install(DownloadInstallManager.InstallHandler.PackageDeleteObserver.this.mAppId, DownloadInstallManager.InstallHandler.PackageDeleteObserver.this.mDownloadId, DownloadInstallManager.InstallHandler.PackageDeleteObserver.this.mApkPath);
	              DownloadInstallManager.this.mLocalAppManager.removeLocalAppListener(DownloadInstallManager.InstallHandler.PackageDeleteObserver.this);
	            }
	          
	        });
	      }
	    }
		 

		class PackageInstallObserver extends IPackageInstallObserver.Stub
		    {
		      private final String mApkPath;		     
		      private final AppInfo mApp;
		      private long mDownloadId;
		      
		      public PackageInstallObserver(AppInfo appinfo, long downloadId, String apkPath)
		      {
		        this.mApp = appinfo;
		        this.mDownloadId = downloadId;		        
		        this.mApkPath = apkPath;
		      }

			@Override
			public void packageInstalled(String packageName, int returnCode) throws RemoteException 
				{
				
						DownloadInstallManager.this.mDownloadManager.remove(DownloadInstallManager.InstallHandler.PackageInstallObserver.this.mDownloadId);
						DownloadInstallManager.this.mAppDownloadMap.remove(Long.valueOf(DownloadInstallManager.InstallHandler.PackageInstallObserver.this.mDownloadId));
						
						//Toast.makeText(DownloadInstallManager.this.mContext,"install complete",Toast.LENGTH_LONG).show();
						Log.v("package installed");
						DownloadInstallManager.InstallHandler.this.installComplete(DownloadInstallManager.InstallHandler.PackageInstallObserver.this.mApp.getId(), DownloadInstallManager.InstallHandler.PackageInstallObserver.this.mDownloadId, 10);
						InstallHandler localInstallHandler = DownloadInstallManager.this.mInstallHandler;
					    localInstallHandler.getClass();
						LocalAppManager.getManager().addLocalAppListener(packageName, new DownloadInstallManager.InstallHandler.PackageDeleteObserver(packageName, this.mDownloadId));
						GlobalApplication.createShortcutForPackage(this.mApp.getPackageName());
				}
		    }
	 }
	
	
	
	private class ProgressHandler extends Handler
	{
	    private Cursor mDownloadCursor;
	    private ContentObserver mDownloadObserver = new ContentObserver(this)
	    {
	      public void onChange(boolean paramAnonymousBoolean)
	      {	    	  
	          DownloadInstallManager.ProgressHandler.this.checkProgress();
	      }
	    };

	    public ProgressHandler(Looper arg2)
	    {
	      super();
	      initialize();
	    }

	    private void checkProgress()
	    {
	      post(new Runnable()
	      {
	        public void run()
	        {
	        	long dlid = 0;
	          if (DownloadInstallManager.ProgressHandler.this.mDownloadCursor == null)
	          {	        	
	        	  DownloadInstallManager.ProgressHandler.this.setupAndEnsureDownloadObserver();	            	              	          
	          }
	          else
	          {	            
	            DownloadInstallManager.ProgressHandler.this.mDownloadCursor.requery();	            
	          }
	          
	          if (DownloadInstallManager.ProgressHandler.this.mDownloadCursor != null)
	            {	            	
	            	int id = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_ID);
		            int status = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS);
		            int reason = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_REASON);
		            int currBytes = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_BYTES_DOWNLOADED_SO_FAR);
		            int totalBytes = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getColumnIndexOrThrow(DownloadManager.COLUMN_TOTAL_SIZE_BYTES);
		            HashSet<Long> hashset = new HashSet<Long>();
		            DownloadInstallManager.ProgressHandler.this.mDownloadCursor.moveToPosition(-1);
		            while (DownloadInstallManager.ProgressHandler.this.mDownloadCursor.moveToNext())
		            {
		              dlid = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getLong(id);
		              Log.v("dlid1:" + dlid);
		              hashset.add(Long.valueOf(dlid));
		              int i1 = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getInt(status);
		              int i2 = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getInt(reason);
		              int i3 = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getInt(currBytes);
		              int i4 = DownloadInstallManager.ProgressHandler.this.mDownloadCursor.getInt(totalBytes);
		              String str = (String)DownloadInstallManager.this.mAppDownloadMap.get(Long.valueOf(dlid));
		              if (!TextUtils.isEmpty(str))
		                DownloadInstallManager.ProgressHandler.this.updateProgress(str, DownloadInstallManager.ProgressHandler.this.translateStatus(i1), i2, i3, i4);
		              
		              
		            }
		            Iterator<Long> localIterator = DownloadInstallManager.this.mAppDownloadMap.keySet().iterator();
		            
		            while (localIterator.hasNext())
		            {
		              Long localLong = (Long)localIterator.next();
		              String appid = (String)DownloadInstallManager.this.mAppDownloadMap.get(Long.valueOf(localLong));
		              AppInfo appinfo = AppInfo.get(appid);
		              
		              Log.v("app download:" + appinfo.getPackageName() + " download id:" + appinfo.getDownloadId());
		              
		              Log.v("check progress:" + localLong);
		              if (!hashset.contains(localLong)){
		            	  Log.v("iterator1:" + localLong);
		            	  Log.v("dlid2:" + dlid);
		            	  if(DownloadInstallManager.this.mAppDownloadMap.contains(localLong)){
		            		  Log.v("mAppDownloadMap.contains");
		            	  }
		            	  else{
		            		  Log.v("! mAppDownloadMap.contains");
		            	  }
		            		  
		            		
		            	  if(DownloadInstallManager.this.mInstallManuallyMap.contains(appinfo.getPackageName()))
		            	  {
		            		  Log.v("mInstallManuallyMap.contains");		            		  
		            	  }else{
		            		  Log.v("!mInstallManuallyMap.contains");
		            	  }
		            		  
		            	//  if(dlid == localLong  && !DownloadInstallManager.this.mInstallManuallyMap.contains(appinfo.getPackageName()))
		            		  DownloadInstallManager.this.handleDownloadComplete(localLong.longValue());  
		              }
		              
		                
		             }
		            
	            }
	        }
	      });
	    }

	    private void initialize()
	    {
	      post(new Runnable()
	      {
	        public void run()
	        {
	          DownloadInstallManager.ProgressHandler.this.setupAndEnsureDownloadObserver();
	        }
	      });
	    }

	 
	    private void setupAndEnsureDownloadObserver()
	    {
	      try
	      {
	        if (this.mDownloadCursor == null)
	        {
	          DownloadManager.Query query = new DownloadManager.Query();
	          query.setFilterByStatus(7);
	          this.mDownloadCursor = DownloadInstallManager.this.mDownloadManager.query(query);
	          if (this.mDownloadCursor != null)
	            this.mDownloadCursor.registerContentObserver(this.mDownloadObserver);
	        }
	        return;
	      }
	      finally
	      {	        
	      }
	    }

	    private int translateStatus(int status)
	    {
	      int i;
	      switch (status)
	      {		      
		      case 1:
		    	  i = 1;
		    	  break;
		      case 4:
		    	  i = 4;
		    	  break;
		      case 2:
		    	  i = 3;
		    	  break;
		      case 3:  
		      default:
		        i = -1;
	      }	      
	      return i;	  
	    }

	    private void updateProgress(String appId, int status, int reason, int currBytes, int totalBytes)
	    {	    	
	      DownloadInstallManager.Progress progress = (DownloadInstallManager.Progress)DownloadInstallManager.this.mCurrProgressMap.get(appId);
	      if (progress == null)
	      {
	        progress = new DownloadInstallManager.Progress();
	        progress.status = -1;
	        DownloadInstallManager.this.mCurrProgressMap.put(appId, progress);
	      }
	      int oldStatus = progress.status;
	      progress.status = status;
	      progress.reason = reason;
	      progress.currBytes = currBytes;
	      progress.totalBytes = totalBytes;
	      CopyOnWriteArraySet<ProgressListener> localCopyOnWriteArraySet = (CopyOnWriteArraySet<ProgressListener>)DownloadInstallManager.this.mProgressListeners.get(appId);
	      if (localCopyOnWriteArraySet != null)
	      {
	        Iterator<ProgressListener> localIterator = localCopyOnWriteArraySet.iterator();
	        while (localIterator.hasNext())
	        {
	          DownloadInstallManager.ProgressListener localProgressListener = (DownloadInstallManager.ProgressListener)localIterator.next();
	          localProgressListener.onProgressUpdate(appId, progress);
	          if (oldStatus != status)
	            localProgressListener.onStateUpdate(appId, status, oldStatus);
	        }
	      }
	    }

	    public void updateProgress(final String appid, final int status)
	    {
	      post(new Runnable()
	      {
	        public void run()
	        {	
	          Log.v("updateProgress");	
	          DownloadInstallManager.ProgressHandler.this.updateProgress(appid, status, 0, 0, 0);
	        }
	      });
	    }
	  }
	  
	public void initData()
	  {
	    this.mProgressThread = new HandlerThread("ProgressThread");
	    this.mProgressThread.start();
	    this.mProgressHandler = new ProgressHandler(this.mProgressThread.getLooper());
	    this.mDownloadTaskManageThread = new HandlerThread("DownloadTaskManageThread");
	    this.mDownloadTaskManageThread.start();
	    this.mDownloadTaskManageHandler = new DownloadTaskManageHandler(this.mDownloadTaskManageThread.getLooper());
	    this.mInstallThread = new HandlerThread("InstallThread");
	    this.mInstallThread.start();
	    this.mInstallHandler = new InstallHandler(this.mInstallThread.getLooper());
	  }
	

	
	 private boolean isInstalledAndNotUpdate(AppInfo paramAppInfo)
	  {
	    if (paramAppInfo.canInstallOrUpdate())
	    	return true;
	    return false;
	  }
	 

	public boolean arrange(AppInfo appInfo)
	 {
		Log.v("arrange:" + appInfo.getName());
		boolean bool = false;
			    
		this.mProgressHandler.setupAndEnsureDownloadObserver();
		   
		    //GlobalApplication.class.wait();
		if(isDownloadingOrInstalling(appInfo.getId()))
		{
			Log.v("isDownloadingOrInstalling");
			if(DownloadInstallManager.this.mInstallManuallyMap.containsKey(appInfo.getPackageName()))
			{
					
					//DownloadInstallManager.this.handleDownloadComplete(appInfo.getDownloadId());
					long dlid = appInfo.getDownloadId();
					Log.v("mAppDownloadMap:" + dlid);
					DownloadManager.Query query = new DownloadManager.Query();
					query.setFilterById(dlid);
					Cursor cursor =  DownloadInstallManager.this.mDownloadManager.query(query);
					if (cursor != null)
					try
					{						
						
						if (cursor.moveToFirst())
						{
							String uri = ""; 
	                		
							if(Build.VERSION.SDK_INT >= 11)
								uri = cursor.getString(cursor.getColumnIndexOrThrow("local_filename"));
							int status = cursor.getInt(cursor.getColumnIndexOrThrow(DownloadManager.COLUMN_STATUS));
							                		
							Log.v("download status: id:" + dlid + " status:" + status + " uri:" + uri);		                		
							if (status != -1)
							{
							  DownloadInstallManager.this.mInstallHandler.install(appInfo.getId(), dlid, uri);
							}
					    }
					}catch(Exception e){
	                	Log.v("handleDownloadComplete exception:" +  e.getMessage());
	                }
	                finally{
	                	if (cursor != null)
	                		cursor.close();
	                }
					
				   /*Iterator<Long> dlid = DownloadInstallManager.this.mAppDownloadMap.keySet().iterator();
			       while(dlid.hasNext()){
			            Long key = dlid.next();
			            String appid = DownloadInstallManager.this.mAppDownloadMap.get(key);
			            if(appid.equals(appInfo.getId())){
			            	
			            }
			        }
			        */
			       
			}
			else{
				Log.v("MarketDownloadInstallManager App " + appInfo.getPackageName() + " already exists");
			}
		    
		    return bool;
		}      
		if(isInstalledAndNotUpdate(appInfo))
		{	         
		   Log.v("MarketDownloadInstallManager App " + appInfo.getPackageName() + " already install and has no updates");
		}
		else
		{
			this.mDownloadTaskManageHandler.arrange(appInfo);
			bool = true;
		}
	     return bool; 
	 }
	
	public boolean isDownloadingOrInstalling(String appId)
	  {
	    synchronized (this.mDownloadInstallApps)
	    {
	      boolean bool = this.mDownloadInstallApps.contains(appId);
	      return bool;
	    }
	  }
	
	
	 public static abstract interface TaskListener
	  {
	    public abstract void onTaskFail(String appid, int errorcode);

	    public abstract void onTaskStart(String appid);

	    public abstract void onTaskSuccess(String appid);
	  }
	 

	
	public static abstract interface ProgressListener
	  {
	    public abstract void onProgressUpdate(String value, DownloadInstallManager.Progress paramProgress);

	    public abstract void onStateUpdate(String paramString, int paramInt1, int paramInt2);
	  }
	public class Progress
	  {
	    public int currBytes;
	    public int reason;
	    public int status;
	    public int totalBytes;

	    public Progress()
	    {
	    }
	  }

	 private class DownloadInstallMonitor
	  {
	    private SparseArray mAutoUpdateResultArray = new SparseArray();
	    private int mAutoUpdateTotalCount = 0;

	    public DownloadInstallMonitor()
	    {
	    }	   
	    public void onFail(String appId, int error)
	    {
	    	if(error == DownloadManager.STATUS_PAUSED)
	    	{
	    		DownloadInstallManager.this.mDownloadTaskManageHandler.post(new Runnable()
	            {
	              public void run()
	              {
	            	 InstallChecker.showNoEnoughSpaceDialog(GlobalApplication._());
	              }
	            });
	    		
	    		//Toast.makeText(GlobalApplication._(), "Tải xuống không thành công. Dung lượng lưu trữ hiện tại không đủ.",Toast.LENGTH_LONG).show();
	    	}
	    	else
	    		Toast.makeText(GlobalApplication._(), "Tải xuống không thành công",Toast.LENGTH_LONG).show();
	    	
	    }

	    public void onStart(String paramString)
	    {
	      
	    }

	    public void onSuccess(String appid)
	    {
	    	AppInfo appinfo = AppInfo.get(appid);	    	
	    	if(LocalAppManager.getManager().isInstalled(appinfo.getPackageName()))
	    	{
	    		GlobalApplication.showInstallSuccessNotification(appinfo);
	    	}
 
	    }
	  }

	public void addTaskListener(TaskListener listenner) {
		Log.v("addTaskListener");
		if(listenner != null)
		{
			synchronized (this.mTaskListeners)
		      {
		        this.mTaskListeners.add(listenner);
		      }
		}		
	}
	
	public void removeTaskListener(TaskListener listenner)
	  {
		if(listenner != null)
		{
			synchronized (this.mTaskListeners)
		      {
		        this.mTaskListeners.remove(listenner);
		      }
		}	
	  }


	
	    
	  
}
